/* Copyright ©, 2022, ECARX (Hubei) Technology Co., Ltd. All rights reserved.
 *
 * NOTICE: This file contains information which is confidential and proprietary to ECARX
 * (Hubei) Technology Co., Ltd. and/or other developers. No license to any intellectual
 * property rights contained within this file is either granted or implied except as may be
 * provided in an agreement with ECARX (Hubei) Technology Co., Ltd.
 *
 * Any unauthorized copying or distribution of content from this file in whole or in part
 * is prohibited.
 */
#ifndef ECARX_VHAL_CORE_PROPERTY_DECALARY_H
#define ECARX_VHAL_CORE_PROPERTY_DECALARY_H

#include <log/log.h>
#include "VHalLog.h"
#include <utils/Looper.h>

#include <memory>
#include <optional>

#include "vhal_core/Common.h"
#include "vhal_core/persist/Properties.h"
#include "vhal_core/property/ReadPropertyBuilder.h"
#include "vhal_core/property/ReadWritePropertyBuilder.h"
#include "vhal_core/property/VirtualPropertyBuilder.h"
#include "vhal_core/property/WritePropertyBuilder.h"
#include "vhal_core/property/IProperty.h"

namespace ecarx::vhal::core::property {

class IPropertyTransfer;
/**
 * @brief Represents an vehicle hal property.
 The framework provides a series of macros to represent it
 * \see by system property has macros SYSTEM_READ_PROPERTY, SYSTEM_CONTINUOUS_PROPERTY,
 SYSTEM_WRITE_PROPERTY, SYSTEM_READ_WRITE_PROPERTY, SYSTEM_STATIC_PROPERTY
 * \see by vendor property has macros VENDOR_READ_PROPERTY, VENDOR_CONTINUOUS_PROPERTY
 VENDOR_WRITE_PROPERTY, VENDOR_READ_WRITE_PROPERTY, VENDOR_STATIC_PROPERTY
 * \see this is a virtual property macro VIRTUAL_PROPERTY
 *
 */
class Property {
  protected:
    IProperty* mProperty;

  private:
    IPropertyTransfer* mTransfer;
    android::sp<android::MessageHandler> mMessageHandler;

  private:
    std::unique_ptr<VehiclePropValue> getPropValue(int32_t propertyId, int32_t areaId) const;
    bool setPropValue(const VehiclePropValue& value) const;
    Area* getArea(int32_t, int32_t) const;

  public:
  /**
   * @brief Construct a new Property object
   * 
   * @param propertyId The property id in types.hal
   * @param proName The propery Name in types.hal
   * @param chanMode The property change mode
   * @param access The propery access
   */
    Property(const int32_t propertyId, const char* proName, VehiclePropertyChangeMode chanMode,
             VehiclePropertyAccess access);

    virtual ~Property();

    void init(IPropertyTransfer* transfer);

    virtual void onInit() {}

    IProperty* toIProperty();

    /**
     * @brief DID interface
     * 
     * @return \ref persist::DID* "DID*"
     */
    persist::DID* getDID();
    
    /**
     * @brief Profile interface
     * 
     * @return \ref persist::PlatformProfile* "PlatformProfile*"
     */
    persist::PlatformProfile* getPlatformProfile();

    /**
     * @brief Get the Current Profile ID value
     * 
     * @return int32_t 
     */
    int32_t getCurrentProfileID();

    /**
     * @brief Get the Property Status
     * 
     * @param propertyId property id
     * @param areaId area id
     * @return \ref PropertyStatus 
     */
    PropertyStatus getPropertyStatus(int32_t propertyId, int32_t areaId) const;

    /**
     * @brief update current property status
     * 
     * @param areaId area id
     * @param status PropertyStatus
     */
    void updateAreaStatus(int32_t areaId, PropertyStatus status);

    /**
     * @brief update current property value
     * 
     * @tparam _TY value type
     * @param areaId area id
     * @param value 
     * @param persist If true, it will be stored in a more attribute supported storage method,default false
     */
    template <typename _TY>
    void setValue(int32_t areaId, const _TY &value, bool persist = false) {
        auto area = mProperty->getArea(areaId == toInt(VehicleArea::GLOBAL) ? 0 : areaId);
        if (area != nullptr) {
            if constexpr (std::is_enum_v<_TY> || std::is_same_v<_TY, int32_t> ||
                          std::is_same_v<_TY, bool>) {
                area->setInt32Value(static_cast<int32_t>(value), persist);
            } else if constexpr (std::is_same_v<_TY, std::vector<int32_t>>) {
                area->setInt32Values(value, persist);
            } else if constexpr (std::is_same_v<_TY, int64_t>) {
                area->setInt64Value(value, persist);
            } else if constexpr (std::is_same_v<_TY, std::vector<int64_t>>) {
                area->setInt64Values(value, persist);
            } else if constexpr (std::is_same_v<_TY, float>) {
                area->setFloatValue(value, persist);
            } else if constexpr (std::is_same_v<_TY, std::vector<float>>) {
                area->setFloatValues(value, persist);
            } else if constexpr (std::is_same_v<_TY, std::string>) {
                area->setStringValue(value, persist);
            } else if constexpr (std::is_same_v<_TY, std::vector<uint8_t>>) {
                area->setBytes(value, persist);
            } else if constexpr (std::is_same_v<_TY, MixedValue>) {
                area->setMixedValue(value, persist);
            } else {
                area->setBytes(utils::toBytes(value), persist);
            }
        }
    }

    /**
     * @brief Get the Property Value 
     * 
     * @tparam _TY 
     * @param propertyId property ID
     * @param areaId 
     * @return _TY 
     */
    template <typename _TY>
    _TY getPropertyValue(int32_t propertyId, int32_t areaId) const {
        auto value = this->getPropValue(propertyId, areaId);
        if (value != nullptr) {
            if constexpr (std::is_enum_v<_TY> || std::is_same_v<_TY, int32_t> ||
                          std::is_same_v<_TY, bool>) {
                return _TY(value->value.int32Values[0]);
            } else if constexpr (std::is_same_v<_TY, std::vector<int32_t>>) {
                return value->value.int32Values;
            } else if constexpr (std::is_same_v<_TY, int64_t>) {
                return value->value.int64Values[0];
            } else if constexpr (std::is_same_v<_TY, std::vector<int64_t>>) {
                return value->value.int64Values;
            } else if constexpr (std::is_same_v<_TY, float>) {
                return value->value.floatValues[0];
            } else if constexpr (std::is_same_v<_TY, std::vector<float>>) {
                return value->value.floatValues;
            } else if constexpr (std::is_same_v<_TY, std::string>) {
                return value->value.stringValue;
            } else if constexpr (std::is_same_v<_TY, std::vector<uint8_t>>) {
                return value->value.bytes;
            } else if constexpr (std::is_same_v<_TY, MixedValue>) {
                return value->value;
            } else {
                return utils::toValue<_TY>(value->value.bytes);
            }
        }
        return {};
    }

    /**
     * @brief Set the Property Value,This method will trigger the onSetProperty event of the calling property
     * 
     * @tparam _TY 
     * @param propertyId 
     * @param areaId 
     * @param value 
     * @return if true call success otherwise false
     */
    template <typename _TY>
    bool setPropertyValue(int32_t propertyId, int32_t areaId, _TY value) const {
        VehiclePropValue propValue;
        propValue.prop = propertyId;
        propValue.areaId = isGlobalProp(propertyId) ? 0 : areaId;
        if constexpr (std::is_same_v<_TY, int32_t> || std::is_same_v<_TY, bool> ||
                      std::is_enum_v<_TY>) {
            propValue.value.int32Values = {static_cast<int32_t>(value)};
        } else if constexpr (std::is_same_v<_TY, std::vector<int32_t>>) {
            propValue.value.int32Values = value;
        } else if constexpr (std::is_same_v<_TY, int64_t>) {
            propValue.value.int64Values = {value};
        } else if constexpr (std::is_same_v<_TY, std::vector<int64_t>>) {
            propValue.value.int64Values = value;
        } else if constexpr (std::is_same_v<_TY, float>) {
            propValue.value.floatValues = {value};
        } else if constexpr (std::is_same_v<_TY, std::vector<float>>) {
            propValue.value.floatValues = value;
        } else if constexpr (std::is_same_v<_TY, std::string>) {
            propValue.value.stringValue = value;
        } else if constexpr (std::is_same_v<_TY, std::vector<uint8_t>>) {
            propValue.value.bytes = value;
        } else if constexpr (std::is_same_v<_TY, MixedValue>) {
            propValue.value = value;
        } else {
            propValue.value.bytes = utils::toBytes(value);
        }
        return setPropValue(propValue);
    }

    /**
     * @brief Send a scheduled message event.
     * 
     * @param msgId event id
     * @param delay_ms unit(ms)
     */
    void sendMsg(uint32_t msgId, uint32_t delay_ms);

    /**
     * @brief remove scheduled message event if exist.
     * 
     * @param msgId event id
     */
    void removeMsg(uint32_t msgId);

    /**
     * @brief Get the property value form the persist
     * 
     * @tparam _TY 
     * @param propertyId 
     * @param areaId 
     * @return property value
     */
    template <typename _TY>
    std::optional<_TY> getPropertyPersistValue(int32_t propertyId, int32_t areaId){
         Area* area = getArea(propertyId, areaId);
         std::optional<_TY> res;
         if(area != nullptr){
            auto& key = area->getKey();
            persist::Properties* persist = area->getPersist();
            if constexpr (std::is_same_v<_TY, int32_t>) {
                if (persist && persist->hasProperty(key)) {
                    res = persist->getInt32(key, -1);
                }
            } else if constexpr (std::is_same_v<_TY, int64_t>) {
                if (persist && persist->hasProperty(key)) {
                    res = persist->getInt64(key, -1);
                }
            } 
         }
         return res;
    }

    /**
     * @brief Set the persistence type of the area.
     *
     * @param areaId area id
     * @param type persistence id
     */
    void setPersistTyp(int32_t areaId, PersistType type);
};

// macro Property Define
#define __concat__(a, b) a##b
#define __merge_middle__(a, b) __concat__(a, b)
#define __uniqueName(name) __merge_middle__(name, __COUNTER__)

#define __PROPERTY_DECLARY_DEFINE(CLASS_NAME, TARGET, PROPERTY_NAME, CHANGE_MODE, ACCESS_MODE,      \
                                  BUILDER, PROP_VALUE_TYPE, VAR)                                    \
    namespace ecarx::vhal::core::property::impl {                                                   \
    class CLASS_NAME : public Property {                                                            \
        BUILDER<PROP_VALUE_TYPE> builder;                                                           \
                                                                                                    \
      public:                                                                                       \
        explicit CLASS_NAME()                                                                       \
            : Property(toInt(TARGET::PROPERTY_NAME), #PROPERTY_NAME,                                \
                       VehiclePropertyChangeMode::CHANGE_MODE, VehiclePropertyAccess::ACCESS_MODE), \
              builder(mProperty) {}                                                                 \
        void onInit() override;                                                                     \
        PROP_VALUE_TYPE getValue(int32_t area) {                                                    \
            return getPropertyValue<PROP_VALUE_TYPE>(toInt(TARGET::PROPERTY_NAME), area);           \
        }                                                                                           \
        PropertyStatus getStatus(int32_t area) {                                                    \
            return getPropertyStatus(toInt(TARGET::PROPERTY_NAME), area);                           \
        }                                                                                           \
        void setValue(int32_t area, const PROP_VALUE_TYPE& val, bool persist = false) {             \
            Property::setValue<PROP_VALUE_TYPE>(area, val, persist);                                \
        }                                                                                           \
    };                                                                                              \
    REGISTER_PROPERTY(CLASS_NAME)                                                                   \
    }                                                                                               \
    void ecarx::vhal::core::property::impl::CLASS_NAME::onInit()

#define __VIRTUAL_PROPERTY_DECLARY(CLASS_NAME, TARGET, PROPERTY_NAME, CHANGE_MODE, ACCESS_MODE,     \
                                   BUILDER)                                                         \
    namespace ecarx::vhal::core::property::impl {                                                   \
    class CLASS_NAME : public Property {                                                            \
        BUILDER<void> builder;                                                                      \
                                                                                                    \
      public:                                                                                       \
        explicit CLASS_NAME()                                                                       \
            : Property(toInt(TARGET::PROPERTY_NAME), #PROPERTY_NAME,                                \
                       VehiclePropertyChangeMode::CHANGE_MODE, VehiclePropertyAccess::ACCESS_MODE), \
              builder(mProperty) {}                                                                 \
        void onInit() override;                                                                     \
    };                                                                                              \
    REGISTER_PROPERTY(CLASS_NAME)                                                                   \
    }                                                                                               \
    void ecarx::vhal::core::property::impl::CLASS_NAME::onInit()

#define __PROPERTY_DECALARY(PROPERTY_NAME, TARGET, CHANGE_MODE, ACCESS_MODE, BUILDER,    \
                            PROP_VALUE_TYPE)                                             \
    __PROPERTY_DECLARY_DEFINE(TARGET##PROPERTY_NAME, TARGET, PROPERTY_NAME, CHANGE_MODE, \
                              ACCESS_MODE, BUILDER, PROP_VALUE_TYPE, __uniqueName(property))

#define __READ_PROPERTY(PROPERTY_NAME, TARGET, PROP_VALUE_TYPE)                      \
    __PROPERTY_DECALARY(PROPERTY_NAME, TARGET, ON_CHANGE, READ, ReadPropertyBuilder, \
                        PROP_VALUE_TYPE)

#define __STATIC_PROPERTY(PROPERTY_NAME, TARGET, PROP_VALUE_TYPE) \
    __PROPERTY_DECALARY(PROPERTY_NAME, TARGET, STATIC, READ, ReadPropertyBuilder, PROP_VALUE_TYPE)

#define __CONTINUOUS_PROPERTY(PROPERTY_NAME, TARGET, PROP_VALUE_TYPE)                 \
    __PROPERTY_DECALARY(PROPERTY_NAME, TARGET, CONTINUOUS, READ, ReadPropertyBuilder, \
                        PROP_VALUE_TYPE)

#define __WRITE_PROPERTY(PROPERTY_NAME, TARGET, PROP_VALUE_TYPE)                       \
    __PROPERTY_DECALARY(PROPERTY_NAME, TARGET, ON_CHANGE, WRITE, WritePropertyBuilder, \
                        PROP_VALUE_TYPE)

#define __READ_WRITE_PROPERTY(PROPERTY_NAME, TARGET, PROP_VALUE_TYPE)                           \
    __PROPERTY_DECALARY(PROPERTY_NAME, TARGET, ON_CHANGE, READ_WRITE, ReadWritePropertyBuilder, \
                        PROP_VALUE_TYPE)

/*!
    \brief A macro that Android property enum VehicleProperty:read only property
*/
#define SYSTEM_READ_PROPERTY(PROPERTY_NAME, PROP_VALUE_TYPE) \
    __READ_PROPERTY(PROPERTY_NAME, VehicleProperty, PROP_VALUE_TYPE)
/*!
    \brief A macro that Android property enum VehicleProperty:static property
*/
#define SYSTEM_STATIC_PROPERTY(PROPERTY_NAME, PROP_VALUE_TYPE) \
    __STATIC_PROPERTY(PROPERTY_NAME, VehicleProperty, PROP_VALUE_TYPE)
/*!
    \brief A macro that Android property enum VehicleProperty:continuous property
*/
#define SYSTEM_CONTINUOUS_PROPERTY(PROPERTY_NAME, PROP_VALUE_TYPE) \
    __CONTINUOUS_PROPERTY(PROPERTY_NAME, VehicleProperty, PROP_VALUE_TYPE)
/*!
    \brief A macro that Android property enum VehicleProperty:write only property
*/
#define SYSTEM_WRITE_PROPERTY(PROPERTY_NAME, PROP_VALUE_TYPE) \
    __WRITE_PROPERTY(PROPERTY_NAME, VehicleProperty, PROP_VALUE_TYPE)
/*!
    \brief A macro that Android property enum VehicleProperty:read wirte property
*/
#define SYSTEM_READ_WRITE_PROPERTY(PROPERTY_NAME, PROP_VALUE_TYPE) \
    __READ_WRITE_PROPERTY(PROPERTY_NAME, VehicleProperty, PROP_VALUE_TYPE)

/*!
    \brief A macro that vendor property enum ECarXVehicleProperty:read only property
*/
#define VENDOR_READ_PROPERTY(PROPERTY_NAME, PROP_VALUE_TYPE) \
    __READ_PROPERTY(PROPERTY_NAME, ECarXVehicleProperty, PROP_VALUE_TYPE)
/*!
    \brief A macro that vendor property enum ECarXVehicleProperty:static property
*/
#define VENDOR_STATIC_PROPERTY(PROPERTY_NAME, PROP_VALUE_TYPE) \
    __STATIC_PROPERTY(PROPERTY_NAME, ECarXVehicleProperty, PROP_VALUE_TYPE)
/*!
    \brief A macro that vendor property enum ECarXVehicleProperty:continuous property
*/
#define VENDOR_CONTINUOUS_PROPERTY(PROPERTY_NAME, PROP_VALUE_TYPE) \
    __CONTINUOUS_PROPERTY(PROPERTY_NAME, ECarXVehicleProperty, PROP_VALUE_TYPE)
/*!
    \brief A macro that vendor property enum ECarXVehicleProperty:write only property
*/
#define VENDOR_WRITE_PROPERTY(PROPERTY_NAME, PROP_VALUE_TYPE) \
    __WRITE_PROPERTY(PROPERTY_NAME, ECarXVehicleProperty, PROP_VALUE_TYPE)
/*!
    \brief A macro that vendor property enum ECarXVehicleProperty:read wirte property
*/
#define VENDOR_READ_WRITE_PROPERTY(PROPERTY_NAME, PROP_VALUE_TYPE) \
    __READ_WRITE_PROPERTY(PROPERTY_NAME, ECarXVehicleProperty, PROP_VALUE_TYPE)

// macro direct transmit property
#define TRANSMIT_READ_PROPERTY(PROPERTY_NAME, PROP_VALUE_TYPE) \
    __READ_PROPERTY(PROPERTY_NAME, DirectTransmitProperty, PROP_VALUE_TYPE)
#define TRANSMIT_WRITE_PROPERTY(PROPERTY_NAME, PROP_VALUE_TYPE) \
    __WRITE_PROPERTY(PROPERTY_NAME, DirectTransmitProperty, PROP_VALUE_TYPE)


/*!
    \brief macro virtual property(Don't define property, Only bussiness logic.)
*/
#define VIRTUAL_PROPERTY(TASK_NAME)                                                  \
    __VIRTUAL_PROPERTY_DECLARY(TASK_NAME, VehicleProperty, INVALID, ON_CHANGE, READ, \
                               VirtualPropertyBuilder)

/// macro task processing function and bind
#define ON_VALUE_SIGNAL_CHANGED(name, __ValueType)                         \
    static __ValueType name([[maybe_unused]] property::Property& property, \
                            [[maybe_unused]] const __ValueType& currentValue)

#define ON_STATUS_SIGNAL_CHANGED(name)                                        \
    static PropertyStatus name([[maybe_unused]] property::Property& property, \
                               [[maybe_unused]] PropertyStatus currentStatus)

#define DECLARY_ON_VALUE_SIGNAL_CHANGED(name, __ValueType) \
    ON_VALUE_SIGNAL_CHANGED(name, __ValueType);
#define DECLARY_ON_STATUS_SIGNAL_CHANGED(name) ON_STATUS_SIGNAL_CHANGED(name);

#define BIND_SIGNAL_FN(fname) std::bind(fname, *this, std::placeholders::_1)

/**
 * @brief Simple value mapping,Sample:
 * @code
MAPPING_VALUE(sdb::AvailabilitySts, PropertyStatus, PropertyStatus::NotAvailable)
    MAPPING_ITEM(sdb::AvailabilitySts::Active, PropertyStatus::Active)
    MAPPING_ITEM(sdb::AvailabilitySts::NotActive, PropertyStatus::NotActive)
    MAPPING_ITEM(sdb::AvailabilitySts::Error, PropertyStatus::Error)
MAPPING_END
 * @endcode 
 * 
 */
#define MAPPING_VALUE(SOURCE_TYPE, TARGET_TYPE, DEFAULT) \
    [[maybe_unused]] static TARGET_TYPE mappingValue(SOURCE_TYPE key, TARGET_TYPE def = DEFAULT) {
#define MAPPING_ITEM(KEY, VALUE) \
    if (key == KEY) return VALUE;

#define MAPPING_END \
    return def;     \
    }

}  // namespace ecarx::vhal::core::property

#endif
